package com.sz.common.core.system.service.impl;

import com.sz.common.base.constants.UserActions;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.EntityUtils;
import com.sz.common.core.service.PrincipalLogUtils;
import com.sz.common.core.service.PrincipalUtils;
import com.sz.common.core.constants.ModuleNames;
import com.sz.common.core.system.dto.MenuItem;
import com.sz.common.core.system.dto.PermissionDto;
import com.sz.common.core.system.entity.Authorization;
import com.sz.common.core.system.entity.Menu;
import com.sz.common.core.system.entity.Permission;
import com.sz.common.core.system.entity.Role;
import com.sz.common.core.system.service.AuthorizationService;
import com.sz.common.core.system.service.MenuService;
import com.sz.common.core.system.service.PermissionService;
import com.sz.common.core.system.service.RoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * Date: 2016-10-26 13:41
 * Author: hpeng.wang <br>
 */
@Service("menuService")
public class MenuServiceImpl extends AbstractService implements MenuService {
    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private AuthorizationService authorizationService;

    @Override
    protected String getMapperNamespace() {
        return "MenuMapper";
    }

    /**
     * @param domainId
     * @param localLanguage
     * @param showAll
     * @return
     */
    @Override
    public MenuItem getMenuTree(int domainId, String localLanguage, boolean showAll, Boolean isTree) {
        ParamData pd = new ParamData();
        pd.put("language", localLanguage);
        pd.put("domainId", domainId);
        List<Menu> menus = dao.findForList(getSqlName("listAllMenus"), pd, Menu.class);
        if (menus.size() > 0) {
            Map<Integer, MenuItem> menuMap = new HashMap<>();
            for (Menu menu : menus) {
                MenuItem item = new MenuItem(menu);
                if (!isTree) {
                    item.setSelected(true);
                }
                menuMap.put(menu.getId(), item);
            }

            MenuItem root = null;
            for (Map.Entry<Integer, MenuItem> entry : menuMap.entrySet()) {
                MenuItem item = entry.getValue();
                if (!showAll && item.isHidden()) {
                    continue;
                }
                int parentId = item.getParentId();
                MenuItem parent = menuMap.get(parentId);
                if (parent != null) { // has parent node
                    parent.addChild(item);
                } else if (parentId == 0) {
                    root = item;
                }
            }
            if (root != null) {
                root.sort();
            }
            return root;
        }
        return null;
    }


    @Override
    public MenuItem getRoleMenuTree(int roleId) {
        String localLanguage = PrincipalUtils.getLocalLanguage();
        Role role = roleService.findByRoleId(roleId);
        if (role == null) {
            return null;
        }
        MenuItem root = this.getMenuTree(role.getDomainId(), localLanguage, false, false);
        //如果为root用户直接获取全部的菜单
        if (roleId == 1) {
            return root;
        }
        //根据roleId集合获取相应的menuId
        List<Authorization> authorizationList =
                authorizationService.findAuthListByRoleIds(Arrays.asList(roleId), 1);//1代表菜单
        List<Integer> authorizedMenuIdList = new ArrayList<>();
        authorizationList.forEach(authorization -> authorizedMenuIdList.add(authorization.getResId()));

        removeUnauthorizedMenu(root, authorizedMenuIdList, false);
        return root;
    }

    private boolean removeUnauthorizedMenuAndPermission(MenuItem item, List<Integer> permittedMenuIds, Boolean isRemove,
                                                        List<Authorization> authorizationPermissionList) {
        if (permittedMenuIds.contains(item.getId())) {
            for (Authorization authorization : authorizationPermissionList) {
                if (authorization.getResId().equals(item.getId())) {
                    List<PermissionDto> list = item.getPermissions();
                    if (!list.isEmpty()) {
                        list.forEach(obj -> {
                            if (authorization.getPermissionId().equals(obj.getId())) {
                                obj.setSelected(true);
                            }
                        });
                    }
                }
            }
            return true;
        }
        boolean keepChild = false;
        if (item.hasChild()) {
            for (MenuItem childItem : item.getChildren()) {
                if (removeUnauthorizedMenuAndPermission(childItem, permittedMenuIds, isRemove, authorizationPermissionList)) {
                    keepChild = true;

                } else {
                    childItem.setSelected(false);
                }
            }
        }
        return keepChild;
    }

    @Override
    public MenuItem getCurrUserMenu() {

        Integer domainId = PrincipalUtils.getDomainId();
        String localLanguage = PrincipalUtils.getLocalLanguage();
        MenuItem root = this.getMenuTree(domainId, localLanguage, false, true);

        //如果为root用户直接获取全部的菜单
        if (PrincipalUtils.isRoot() || PrincipalUtils.isAdmin()) {
            return root;
        }
        // 获取当前用户的权限列表
        List<Role> roleList = roleService.getRoleByMember(PrincipalUtils.getRoleMembership());
        if (CollectionUtils.isEmpty(roleList)) {
            return null;
        }
        // 根据权限对象集获取对应的角色id集合
        List<Integer> roleIds = new ArrayList<>();
        roleList.forEach(role -> roleIds.add(role.getId()));

        //根据roleId集合获取相应的menuId
        List<Authorization> authorizationList = authorizationService.findAuthListByRoleIds(roleIds, 1);//1代表菜单
        List<Integer> authorizedMenuIdList = new ArrayList<>();
        authorizationList.forEach(authorization -> authorizedMenuIdList.add(authorization.getResId()));

        removeUnauthorizedMenu(root, authorizedMenuIdList, true);
        return root;
    }

    /**
     * @param item
     * @param permittedMenuIds
     * @return
     */
    private boolean removeUnauthorizedMenu(MenuItem item, List<Integer> permittedMenuIds, Boolean isRemove) {
        if (permittedMenuIds.contains(item.getId())) {
            return true;
        }
        boolean keepChild = false;
        if (item.hasChild()) {
            List<MenuItem> needRemoveItems = new ArrayList<>();
            for (MenuItem childItem : item.getChildren()) {
                if (removeUnauthorizedMenu(childItem, permittedMenuIds, isRemove)) {
                    keepChild = true;
                } else {
                    if (isRemove) {
                        needRemoveItems.add(childItem);
                    } else {
                        childItem.setSelected(false);
                    }
                }
            }
            if (needRemoveItems.size() > 0) {
                item.getChildren().removeAll(needRemoveItems);
            }
        }
        return keepChild;
    }


    @Override
    public List<Menu> findByIds(List<Integer> resIdList, Boolean isHidden) {
        List<Menu> menuList = new ArrayList<>();
        if (resIdList != null && resIdList.size() > 0) {
            //构造查询参数
            ParamData paramData = new ParamData();
            paramData.put("isHidden", isHidden);
            paramData.put("resIdList", resIdList);
            //查询数据
            menuList = dao.findForList(getSqlName("findByIds"), paramData, Menu.class);
        }
        return menuList;
    }

    @Override
    public List<Menu> findByParentIds(List<Integer> parentIds) {
        List<Menu> menuList = new ArrayList<>();
        if (parentIds != null && parentIds.size() > 0) {
            menuList = dao.findForList(getSqlName("findByParentIds"), parentIds, Menu.class);
        }
        return menuList;
    }

    /**
     * 递归查询子菜单.
     *
     * @param menuList1 资源id的list
     * @param isHidden  false 表示显示，true表示隐藏 如果为NULL，代表不需要传这个参数
     * @param menus     递归后要返回的集合数据
     */
    private void recursiveMenu(List<Menu> menuList1, Boolean isHidden, List<Menu> menus) {
        menuList1.forEach(menu -> menus.add(menu));
        List<Integer> ids = EntityUtils.getIntPropListFromBeans(menuList1, "id");
        List<Menu> menuList = this.findByParentIds(ids);

        // 拼装下次查询要用的键集合，并且把已查询出来的放入map中
        if (menuList != null && menuList.size() > 0) {
            recursiveMenu(menuList, isHidden, menus);
        }

    }


    @Override
    public int save(Menu menu) {
        int count = 0;
        if (menu != null) {
            dao.save(getSqlName("insertSelective"), menu);
            //保存操作日志
            Map<String, Object> dataBackup = new HashMap<>();
            dataBackup.put("ADD_MENU", menu);
            PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_MENU, UserActions.ADD, "新建菜单", dataBackup);
            count = menu.getId();
        }
        return count;

    }


    @Override
    public Menu findById(Integer id) {
        Menu menu = null;
        if (id != null && id > 0) {
            menu = (Menu) dao.findForObject(getSqlName("findById"), id);
        }
        return menu;
    }

    @Override
    public int delete(Integer id) {
        int count = 0;
        if (id != null && id > 0) {
            Menu menu = this.findById(id);
            if (menu != null) { //当菜单不为NULL
                if (menu.getIsSystem()) { //判断是否为系统菜单,如果为系统菜单，对系统菜单进行隐藏操作
                    menu.setIsHidden(true);
                    count = this.update(menu);
                } else { //非系统菜单，直接把菜单删除（先解除授权，然后再删除菜单）
                    //获取要删除菜单与子菜单的集合
                    List<Menu> menus = new ArrayList<>();
                    recursiveMenu(Arrays.asList(menu), false, menus);

                    //解除角色中相应的菜单
                    authorizationService.deleteAuthByResId(EntityUtils.getIntPropListFromBeans(menus, "resId"));

                    //删除相应的菜单与子菜单集
                    this.batchdelete(EntityUtils.getIntPropListFromBeans(menus, "id"));
                }
            }
            //保存操作日志
            Map<String, Object> dataBackup = new HashMap<>();
            dataBackup.put("DELETE_MENU", id);
            PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_MENU, UserActions.DELETE, "删除菜单", dataBackup);
        }
        return count;
    }

    private void batchdelete(List<Integer> menuIds) {
        if (!CollectionUtils.isEmpty(menuIds)) {
            dao.delete(getSqlName("deleteByIds"), menuIds);
            //保存操作日志
            Map<String, Object> dataBackup = new HashMap<>();
            dataBackup.put("DELETE_MENU", menuIds);
            PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_MENU, UserActions.DELETE, "删除菜单", dataBackup);
        }
    }

    @Override
    public int update(Menu menu) {
        int count = 0;
        if (!ObjectUtils.isEmpty(menu)) {
            count = (int) dao.update(getSqlName("updateByPrimaryKeySelective"), menu);
            //保存操作日志
            Map<String, Object> dataBackup = new HashMap<>();
            dataBackup.put("UPDATE_MENU", menu);
            PrincipalLogUtils.addOperationLog(ModuleNames.SYS, ModuleNames.SYS_MENU, UserActions.UPDATE, "修改菜单", dataBackup);
        }
        return count;
    }

    @Override
    public MenuItem getMenuPermissionByMenuItemAndDomainId(MenuItem menuItem, int domainId) {
        if (menuItem != null) {
            List<Permission> list = permissionService.findPermissionListByMenuId(menuItem.getId(), domainId);
            if (list != null) {
                List<PermissionDto> permissionDtos = new ArrayList<>();
                for (Permission permission : list) {
                    PermissionDto pdto = new PermissionDto();
                    BeanUtils.copyProperties(permission, pdto);
                    pdto.setMenuId(menuItem.getId());
                    permissionDtos.add(pdto);
                }
                menuItem.setPermissions(permissionDtos);
            }
            if (menuItem.hasChild()) {
                List<MenuItem> childMenuItems = menuItem.getChildren();
                for (MenuItem childMenuItem : childMenuItems) {
                    getMenuPermissionByMenuItemAndDomainId(childMenuItem, domainId);
                }
            }
        }
        return menuItem;
    }

    @Override
    public MenuItem getRoleMenuAndPermissionTree(int roleId) {
        String localLanguage = PrincipalUtils.getLocalLanguage();
        Role role = roleService.findByRoleId(roleId);
        if (role == null) {
            return null;
        }
        MenuItem root = this.getMenuTree(role.getDomainId(), localLanguage, false, false);
        //如果为root用户直接获取全部的菜单
        if (roleId == 1) {
            return root;
        }
        getMenuPermissionByMenuItemAndDomainId(root, role.getDomainId());

        //根据roleId集合获取相应的menuId
        List<Authorization> authorizationList =
                authorizationService.findAuthListByRoleIds(Arrays.asList(roleId), 1);//1代表菜单
        List<Integer> authorizedMenuIdList = new ArrayList<>();
        authorizationList.forEach(authorization -> authorizedMenuIdList.add(authorization.getResId()));
        List<Authorization> authorizationPermissionList =
                authorizationService.findAuthListByRoleIds(Arrays.asList(roleId), 2);//1代表菜单

        removeUnauthorizedMenuAndPermission(root, authorizedMenuIdList, false, authorizationPermissionList);
        return root;
    }
}
